<?php
/** 
  *
  * Copyright (c) 2009, Persistent Systems Limited
  *
  * Redistribution and use, with or without modification, are permitted 
  *  provided that the following  conditions are met:
  *   - Redistributions of source code must retain the above copyright notice, 
  *     this list of conditions and the following disclaimer.
  *   - Neither the name of Persistent Systems Limited nor the names of its contributors 
  *     may be used to endorse or promote products derived from this software 
  *     without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
  
/**
  *
  * This file contains the definition of PHPDataSvcUtil class.
  */

set_error_handler("PHPDataSvcUtilWarningHandler", E_WARNING);
 
if (count($argv) <= 1) 
{
    echo "\nInvalid Arguments.\n";
    ShowUsage();
    exit(0);
}

$uri = stristr($argv[1], "/uri:");
if (empty($uri))
{
    $metadataFile = stristr($argv[1], "/metadata:");
    
    if (empty($metadataFile))
    {
        echo "\nInvalid Arguments. Valid data service uri or service metadata file is required.\n";
        ShowUsage();
        exit(0);
    }
    else
    {
        // Get metadata file from the argument. 
        // Here ":" is guarantted to be there in the $metadataFile
        $metadataFile = substr($metadataFile, strpos($metadataFile, ":") + 1);
        
        // Metadata uri is same metadata file
        $metadataUri = $metadataFile;
        
        // Default Service URI is not known at this point
        $defaultServiceURI = "";
    }
}
else
{
    // Get uri from the argument. 
    // Here ":" is guarantted to be there in the $uri
    $defaultServiceURI = substr($uri, strpos($uri, ":") + 1);
    
    // Prepare metadata uri by appending $metadata to the base uri
    $metadataUri = $defaultServiceURI . "/$" . "metadata";
}

if (count($argv) > 2) 
{
    $outputDir = stristr($argv[2], "/out:");
    if (empty($outputDir))
    {
        echo "\nInvalid Arguments.\n";
        ShowUsage();
        exit(0);
    }
    
    // Here ":" is guarantted to be there in the $metadataFile
    $outputDir = substr($outputDir, strpos($outputDir, ":") + 1);
}
else 
{
   /**
    * if user not passed output path, set it as
    * current directory.
    */
    $outputDir = ".";
}

try
{
    $dataService = new PHPDataSvcUtil($metadataUri, $defaultServiceURI, $outputDir);
    $dataService->generateProxy();
}
catch (Exception $e)
{
    echo "Error generating proxy classes. " . $e->getMessage() . "\n";
}

/**
 * Custom warning handler
 */
function PHPDataSvcUtilWarningHandler($errno, $errstr) 
{
    echo "Error. " . $errstr . "\n";
    exit(1);
}

/**
 * Show PHPDataSvcUtil usage
 */
function ShowUsage()
{
    echo "Usage: php <PHP Toolkit for ADO .NET Data Services path>\PHPDataSvcUtil.php [/uri:<data service Uri> | /metadata:<service metadata file>] [/out:<output file path>]\n";
}

/**
  * The PHPDataSvcUtil class.
  * This class is used to generate the proxy class.
  */
class PHPDataSvcUtil
{
    protected $_metadataUri = "";
    protected $_defaultServiceURI = "";
    protected $_outputPath;
    protected $_serviceMetaDataXmlDoc = null;
    protected $_outputDir = "";

    /**
     * Create an instance of PHPDataSvcUtil
     * @param string $uri The svc uri.
     * @param string $outputDir The path where the proxy class
     * is to be generated.
     */
    public function __construct($metadataUri, $defaultServiceURI, $outputDir) 
    {
        $this->_metadataUri = $metadataUri;
        $this->_baseServiceURI = $defaultServiceURI;
        
        $this->_outputDir = $outputDir;
        $this->_serviceMetaDataXmlDoc = new DOMDocument();
        $this->_serviceMetaDataXmlDoc->load($this->_metadataUri);        
        $this->_outputPath = $outputDir;
    }

    /**
     * Function which generates the proxy file.
     */
    public function generateProxy() 
    {
        // Load xsl file
        $xslDoc = new DOMDocument();        
        $xsl_path = get_cfg_var('adodotnetservicesphp_path');
        if (strlen($xsl_path) == 0)
        {
            throw new Exception("The configuration option 'adodotnetservicesphp_path' is not set in the php.ini file, Please refer installation instructions for fix this issue");
        }
        
        $serviceVersion = $this->getDataServiceVersion();
        if(isset($serviceVersion) && ((int)$serviceVersion > (int)"1.0"))
        {
            throw new Exception("PHPDataSvcUtil can generate code only for Meta data version 1.0, but meta data version of this service is " . $serviceVersion);
        }
        
        $xsl_path = $xsl_path . "/" . "ADODotNETDataServices2PHPProxy.xsl";
        $xslDoc->load($xsl_path);
        
        // Create xslt processor
        $proc = new XSLTProcessor();
        $proc->importStylesheet($xslDoc);
        
        // Set default service URL in xslt
        $proc->setParameter('', 'DefaultServiceURI', $this->_baseServiceURI);
        
        if ($this->_outputPath == ".") 
        {
            $this->_outputPath = $this->_outputPath . "/";
        }
        else if (strlen($this->getPath('basename')) != 0) 
        {
            $index = $this->reverseFind($this->_outputPath, '\\');
            $this->_outputPath = substr($this->_outputPath, 0, $index + 1 );
        } 
        else if ($this->_outputPath[strlen($this->_outputPath) - 1] != '\\') 
        {
            $this->_outputPath = $this->_outputPath . "/";
        }        
        
        $proc->transformToURI($this->_serviceMetaDataXmlDoc, $this->_outputPath . $this->getClassName());
        
    }
   
    /**
     * This function is used to get information
     * of the path $this->_outputDir
     * @param string $type The type information to be fetched
     * @return string output The requested information
     */
    protected function getPath($type)
    {
        $output = "";
        if ($this->_outputDir == ".") 
        {
            return $output;
        }       
        if (strlen($this->_outputDir) != 0) 
        {
            if ($this->_outputDir[strlen($this->_outputDir) - 1] == '\\') 
            {
                $this->_outputDir = substr($this->_outputDir, 0, strlen($this->_outputDir) - 1);
            }            
            $path_parts = pathinfo($this->_outputDir);
            foreach ($path_parts as $key=>$value) 
            {
                if ($key == 'extension') 
                {                    
                    return $path_parts[$type];
                }
            }
        }
        return $output;
    }
    
    /**
     * This function will generate name of the proxy
     * file from the container name.
     * @return string The php proxy file name.
     */
    protected function getClassName()
    {
        // Default class name to be returned
        $containerName = "proxy.php";
        
        $fileName = $this->getPath('basename');
        if (strlen($fileName) != 0) 
        {
            return $fileName;
        }        
        
        $reader = new XMLReader();
        $reader->open($this->_metadataUri);

        while ($reader->read()) 
        {
            if ($reader->nodeType == XMLReader::ELEMENT) 
            {
                if ($reader->name == "EntityContainer") 
                {
                    while ($reader->moveToNextAttribute()) 
                    {
                        if ($reader->name == "Name") 
                        {
                            return $reader->value . ".php";
                        }
                    }
                }
            }
        }
        
        return $containerName;
    }
    
    protected function getDataServiceVersion()
    {
        $reader = new XMLReader();
        $reader->open($this->_metadataUri);

        while ($reader->read()) 
        {
            if ($reader->nodeType == XMLReader::ELEMENT) 
            {
                if ($reader->name == "edmx:Edmx") 
                {
                    while ($reader->moveToNextAttribute()) 
                    {
                        if ($reader->name == "Version") 
                        {
                            return $reader->value;
                        }
                    }
                }
            }
        }
        return null;
    }
    
   /**
     * Function to find the index of last occurance of a
     * character in a string.
     * @param string $string The string to be serached.
     * @param char $char The character to be serached.
     * @return index of $char in $string, -1 if not found.
     */
    protected function reverseFind($string, $char)
    {
        if (($len = strlen($string)) == 0)
        {
            return -1;
        }
    
        $revString = strrev($string);
        if ($revString[0] == $char)
        {
            return ($len - 1);
        }
        
        $index = strpos($revString, $char);
        if ($index != FALSE) 
        {
            return ($len - $index - 1);
        }
        
        return -1;
    }
}
?>